package com.richard.library.http.manager;

import androidx.annotation.NonNull;

import com.franmontiel.persistentcookiejar.ClearableCookieJar;
import com.franmontiel.persistentcookiejar.PersistentCookieJar;
import com.franmontiel.persistentcookiejar.cache.SetCookieCache;
import com.franmontiel.persistentcookiejar.persistence.SharedPrefsCookiePersistor;
import com.richard.library.context.AppContext;
import com.richard.library.http.converter.FastJsonConverterFactory;
import com.richard.library.http.https.CertificateIgnoreSSLParams;
import com.richard.library.http.https.HttpsUtils;
import com.richard.library.http.https.SSLParams;
import com.richard.library.http.request.BasicAPIService;
import com.richard.library.http.verifier.HttpHostnameVerifier;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;

import javax.net.ssl.X509TrustManager;

import okhttp3.Interceptor;
import okhttp3.OkHttpClient;
import retrofit2.Retrofit;

/**
 * <pre>
 * Description : http请求客户端
 * Author : admin-richard
 * Date : 2016/12/19 15:46
 * Changelog:
 * Version            Date            Author              Detail
 * ----------------------------------------------------------------------
 * 1.0         2016/12/19 15:46     admin-richard         new file.
 * </pre>
 */
public final class RetrofitClient {

    private final List<Interceptor> interceptorList = new ArrayList<>();
    private final Config config = new Config();
    private String baseURL = "http://www/";
    private SSLParams httpSSLParams = null;
    private volatile Retrofit normalRetrofit;
    private volatile Retrofit notRedirectRetrofit;
    private BasicAPIService apiService;
    private BasicAPIService notRedirectAPIService;

    private RetrofitClient() {
    }

    private static final class InstanceHolder {
        static final RetrofitClient instance = new RetrofitClient();
    }

    public static RetrofitClient getInstance() {
        return InstanceHolder.instance;
    }

    /**
     * 获取对应的Service
     *
     * @param service Service 的 class
     */
    public <T> T create(boolean followRedirect, Class<T> service) {
        return this.getRetrofit(followRedirect).create(service);
    }

    /**
     * 获取API service
     */
    public BasicAPIService getBaseAPIService(boolean followRedirect) {
        if (this.isCanInitAPIService(followRedirect)) {
            synchronized (RetrofitClient.class) {
                if (this.isCanInitAPIService(followRedirect)) {
                    if (apiService == null && followRedirect) {
                        apiService = this.create(true, BasicAPIService.class);
                    } else if (notRedirectAPIService == null && !followRedirect) {
                        notRedirectAPIService = this.create(false, BasicAPIService.class);
                    }
                }
            }
        }

        return followRedirect ? apiService : notRedirectAPIService;
    }

    /**
     * 配置相关参数
     */
    public Config config(){
        return config;
    }

    /**
     * 获取retrofit
     *
     * @param followRedirect 是否可以重定向
     */
    @NonNull
    public Retrofit getRetrofit(boolean followRedirect) {

        if (this.isCanInitRetrofit(followRedirect)) {
            synchronized (RetrofitClient.class) {
                if (this.isCanInitRetrofit(followRedirect)) {
                    Retrofit retrofit = new Retrofit.Builder()
                            .baseUrl(baseURL)
                            .client(this.generatorHttpClient(followRedirect, followRedirect))
                            .addConverterFactory(FastJsonConverterFactory.create())
                            .build();

                    if (normalRetrofit == null && followRedirect) {
                        normalRetrofit = retrofit;
                    } else if (notRedirectRetrofit == null && !followRedirect) {
                        notRedirectRetrofit = retrofit;
                    }
                }
            }
        }

        return followRedirect ? normalRetrofit : notRedirectRetrofit;
    }


    /**
     * 生成httpClient实例
     *
     * @param followRedirects    是否开启重定向
     * @param followSslRedirects 是否开启ssl重定向
     */
    private OkHttpClient generatorHttpClient(boolean followRedirects, boolean followSslRedirects) {
        OkHttpClient.Builder okHttpClientBuilder = new OkHttpClient.Builder();

        //默认忽略所有https请求证书验证
        if (null == httpSSLParams) {
            httpSSLParams = CertificateIgnoreSSLParams.getSSLParams();
        }

        //添加自定义拦截器
        if (!interceptorList.isEmpty()) {
            for (Interceptor interceptor : interceptorList) {
                okHttpClientBuilder.addInterceptor(interceptor);
            }
        }

        //添加缓存拦截器
//        okHttpClientBuilder.addInterceptor(new CacheInterceptor());
//        // 指定缓存路径,缓存大小 50Mb
//        Cache cache = new Cache(new File(AppContext.get().getCacheDir(), "HttpCache"),
//                1024 * 1024 * 50);

        // Cookie 持久化
        ClearableCookieJar cookieJar = new PersistentCookieJar(
                new SetCookieCache()
                , new SharedPrefsCookiePersistor(AppContext.get())
        );

        //初始化网络请求框架参数
        okHttpClientBuilder
//                .cache(cache)
                .cookieJar(cookieJar)
                .followRedirects(followRedirects)
                .followSslRedirects(followSslRedirects)
                .sslSocketFactory(
                        httpSSLParams.getsSLSocketFactory()
                        , (X509TrustManager) httpSSLParams.getTrustManager()
                )
                .hostnameVerifier(new HttpHostnameVerifier(baseURL))
                .retryOnConnectionFailure(true)//是否开启重连机制
                .connectTimeout(config.connectTimeout, config.timeUnit)//设置超时时间
                .readTimeout(config.readTimeout, config.timeUnit)//设置读取超时时间
                .writeTimeout(config.writeTimeout, config.timeUnit);//设置写的超时时间

        return okHttpClientBuilder.build();
    }

    /**
     * 是否可以初始化APIService
     *
     * @param followRedirect 是否可重定向
     */
    private boolean isCanInitAPIService(boolean followRedirect) {
        return followRedirect && apiService == null
                || !followRedirect && notRedirectAPIService == null;
    }

    /**
     * 是否可以初始化Retrofit
     *
     * @param followRedirect 是否可重定向
     */
    private boolean isCanInitRetrofit(boolean followRedirect) {
        return followRedirect && normalRetrofit == null
                || !followRedirect && notRedirectRetrofit == null;
    }

    //----------------------------------------------------------------------------------------------

    /**
     * 初始化忽略所有https请求证书
     */
    public void initIgnoreCer() {
        this.httpSSLParams = CertificateIgnoreSSLParams.getSSLParams();
    }

    /**
     * https的全局自签名证书
     *
     * @param certificates 证书
     */
    public void initPublicCustomCer(InputStream... certificates) {
        this.httpSSLParams = HttpsUtils.getSslSocketFactory(null, null, certificates);
    }


    /**
     * https双向认证证书
     *
     * @param bksFile      BKS证书文件流
     * @param password     密码
     * @param certificates cert证书文件流
     */
    public void initTwoWayCer(InputStream bksFile, String password, InputStream... certificates) {
        this.httpSSLParams = HttpsUtils.getSslSocketFactory(bksFile, password, certificates);
    }

    /**
     * 设置BaseURL
     *
     * @param baseURL 根域名HOST
     */
    public void setBaseURL(String baseURL) {
        this.baseURL = baseURL;
    }


    /**
     * 添加retrofit 网络请求拦截器
     *
     * @param interceptors 拦截器
     */
    public void addInterceptor(Interceptor... interceptors) {
        interceptorList.addAll(Arrays.asList(interceptors));
    }

    /**
     * 配置相关
     */
    public static class Config {

        /**超时时间单位*/
        private TimeUnit timeUnit = TimeUnit.SECONDS;

        /**连接超时时间*/
        private long connectTimeout = 10;

        /**读取超时时间*/
        private long readTimeout = 60;

        /**写入超时时间*/
        private long writeTimeout = 60;

        public Config timeUnit(@NonNull TimeUnit timeUnit){
            this.timeUnit = timeUnit;
            return this;
        }

        public Config connectTimeout(long timeout){
            this.connectTimeout = timeout;
            return this;
        }

        public Config readTimeout(long timeout){
            this.readTimeout = timeout;
            return this;
        }

        public Config writeTimeout(long timeout){
            this.writeTimeout = timeout;
            return this;
        }
    }

}